[HVM] Move RTC emulation into the hypervisor.
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Wed, 18 Oct 2006 17:35:21 +0000 (18:35 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Wed, 18 Oct 2006 17:35:21 +0000 (18:35 +0100)
Signed-off-by: Xiaowei Yang <xiaowei.yang@intel.com>
18 files changed:
tools/ioemu/Makefile.target
tools/ioemu/target-i386-dm/rtc-dm.c [new file with mode: 0644]
xen/arch/x86/hvm/Makefile
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/i8254.c
xen/arch/x86/hvm/i8259.c
xen/arch/x86/hvm/intercept.c
xen/arch/x86/hvm/io.c
xen/arch/x86/hvm/rtc.c [new file with mode: 0644]
xen/arch/x86/hvm/svm/intr.c
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/hvm/vmx/vmx.c
xen/arch/x86/time.c
xen/common/Makefile
xen/common/time.c [new file with mode: 0644]
xen/include/asm-x86/hvm/vpic.h
xen/include/asm-x86/hvm/vpit.h
xen/include/xen/time.h

index 7af1fdd64650d97b1f34a1ef30fccf37cd3ffd37..b216e6acaf63343589553fc25457101db9be2513 100644 (file)
@@ -294,7 +294,11 @@ OBJS+=gdbstub.o
 endif
 
 # qemu-dm objects
+ifeq ($(ARCH),ia64)
 LIBOBJS=helper2.o exec-dm.o i8259-dm.o
+else
+LIBOBJS=helper2.o exec-dm.o i8259-dm.o rtc-dm.o
+endif
 
 all: $(PROGS)
 
@@ -354,7 +358,11 @@ VL_OBJS+= ne2000.o rtl8139.o pcnet.o
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
 VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
+ifeq ($(ARCH),ia64)
 VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o
+else
+VL_OBJS+= fdc.o serial.o pc.o
+endif
 VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o piix_pci.o
 VL_OBJS+= usb-uhci.o
 VL_OBJS+= piix4acpi.o
diff --git a/tools/ioemu/target-i386-dm/rtc-dm.c b/tools/ioemu/target-i386-dm/rtc-dm.c
new file mode 100644 (file)
index 0000000..e8be999
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * QEMU MC146818 RTC emulation
+ * 
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vl.h"
+
+//#define DEBUG_CMOS
+
+struct RTCState {
+    uint8_t cmos_data[128];
+    uint8_t cmos_index;
+};
+
+void rtc_set_memory(RTCState *s, int addr, int val)
+{
+    if (addr >= 0 && addr <= 127)
+        s->cmos_data[addr] = val;
+}
+
+static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    RTCState *s = opaque;
+
+    if ((addr & 1) == 0) {
+        s->cmos_index = data & 0x7f;
+    } else {
+#ifdef DEBUG_CMOS
+        printf("cmos: write index=0x%02x val=0x%02x\n",
+               s->cmos_index, data);
+#endif        
+        s->cmos_data[s->cmos_index] = data;
+    }
+}
+
+static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
+{
+    RTCState *s = opaque;
+    int ret;
+    if ((addr & 1) == 0) {
+        return 0xff;
+    } else {
+        ret = s->cmos_data[s->cmos_index];
+#ifdef DEBUG_CMOS
+        printf("cmos: read index=0x%02x val=0x%02x\n",
+               s->cmos_index, ret);
+#endif
+        return ret;
+    }
+}
+
+static void rtc_save(QEMUFile *f, void *opaque)
+{
+    RTCState *s = opaque;
+
+    qemu_put_buffer(f, s->cmos_data, 128);
+    qemu_put_8s(f, &s->cmos_index);
+}
+
+static int rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+    RTCState *s = opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_buffer(f, s->cmos_data, 128);
+    qemu_get_8s(f, &s->cmos_index);
+
+    return 0;
+}
+
+RTCState *rtc_init(int base, int irq)
+{
+    RTCState *s;
+
+    s = qemu_mallocz(sizeof(RTCState));
+    if (!s)
+        return NULL;
+
+    register_ioport_write(base, 2, 1, cmos_ioport_write, s);
+    register_ioport_read(base, 2, 1, cmos_ioport_read, s);
+
+    register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
+    return s;
+}
+
+void rtc_set_date(RTCState *s, const struct tm *tm) {}
index 0f6e80f64443d36679e20de8024b27fc11f5235f..37623ff5ebba6a99b3e1a493180ca7b749357ff5 100644 (file)
@@ -4,6 +4,7 @@ subdir-y += vmx
 obj-y += hvm.o
 obj-y += i8254.o
 obj-y += i8259.o
+obj-y += rtc.o
 obj-y += instrlen.o
 obj-y += intercept.o
 obj-y += io.o
index 06b8de63d95a269f5306c6f53b390bf0f0de3459..34afeb46e641857d19588f45528b86f202e6f19e 100644 (file)
 #include <asm/processor.h>
 #include <asm/types.h>
 #include <asm/msr.h>
+#include <asm/mc146818rtc.h>
 #include <asm/spinlock.h>
 #include <asm/hvm/hvm.h>
+#include <asm/hvm/vpit.h>
 #include <asm/hvm/support.h>
 #include <public/sched.h>
 #include <public/hvm/ioreq.h>
@@ -277,6 +279,7 @@ void hvm_setup_platform(struct domain* d)
     init_timer(&platform->pl_time.periodic_tm.timer,
                pt_timer_fn, v, v->processor);
     pit_init(v, cpu_khz);
+    rtc_init(v, RTC_PORT(0), RTC_IRQ);
 }
 
 void pic_irq_request(void *data, int level)
@@ -368,7 +371,7 @@ void hvm_hlt(unsigned long rflags)
 {
     struct vcpu *v = current;
     struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
-    s_time_t next_pit = -1, next_wakeup;
+    s_time_t next_pt = -1, next_wakeup;
 
     /*
      * If we halt with interrupts disabled, that's a pretty sure sign that we
@@ -379,10 +382,10 @@ void hvm_hlt(unsigned long rflags)
         return hvm_vcpu_down();
 
     if ( !v->vcpu_id )
-        next_pit = get_scheduled(v, pt->irq, pt);
+        next_pt = get_scheduled(v, pt->irq, pt);
     next_wakeup = get_apictime_scheduled(v);
-    if ( (next_pit != -1 && next_pit < next_wakeup) || next_wakeup == -1 )
-        next_wakeup = next_pit;
+    if ( (next_pt != -1 && next_pt < next_wakeup) || next_wakeup == -1 )
+        next_wakeup = next_pt;
     if ( next_wakeup != - 1 ) 
         set_timer(&current->arch.hvm_vcpu.hlt_timer, next_wakeup);
     do_sched_op_compat(SCHEDOP_block, 0);
index 6b817293b93204440f5c5bf7394b7ca3cd072f26..5f27ee25b2d8a6837ace094b1b4a825a8592519f 100644 (file)
@@ -49,7 +49,6 @@
 #define RW_STATE_WORD0 3
 #define RW_STATE_WORD1 4
 
-#define ticks_per_sec(v)      (v->domain->arch.hvm_domain.tsc_frequency)
 static int handle_pit_io(ioreq_t *p);
 static int handle_speaker_io(ioreq_t *p);
 
@@ -77,17 +76,6 @@ uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
     return res.ll;
 }
 
-/*
- * get processor time.
- * unit: TSC
- */
-int64_t hvm_get_clock(struct vcpu *v)
-{
-    uint64_t  gtsc;
-    gtsc = hvm_get_guest_time(v);
-    return gtsc;
-}
-
 static int pit_get_count(PITChannelState *s)
 {
     uint64_t d;
@@ -215,11 +203,11 @@ static inline void pit_load_count(PITChannelState *s, int val)
     switch (s->mode) {
         case 2:
             /* create periodic time */
-            s->pt = create_periodic_time (s, period, 0, 0);
+            s->pt = create_periodic_time (period, 0, 0, pit_time_fired, s);
             break;
         case 1:
             /* create one shot time */
-            s->pt = create_periodic_time (s, period, 0, 1);
+            s->pt = create_periodic_time (period, 0, 1, pit_time_fired, s);
 #ifdef DEBUG_PIT
             printk("HVM_PIT: create one shot time.\n");
 #endif
index d54e7ae146599b7cb7c2f30d11297d66b7446a7e..64689e4a8d2c83ffb406cfbdce916d3b12a604ad 100644 (file)
@@ -598,23 +598,47 @@ int cpu_get_pic_interrupt(struct vcpu *v, int *type)
     return intno;
 }
 
-int is_pit_irq(struct vcpu *v, int irq, int type)
+int is_periodic_irq(struct vcpu *v, int irq, int type)
 {
-    int pit_vec;
+    int vec;
+    struct periodic_time *pt =
+        &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
+    struct RTCState *vrtc =
+        &(v->domain->arch.hvm_domain.pl_time.vrtc);
+
+    if (pt->irq == 0) { /* Is it pit irq? */
+        if (type == APIC_DM_EXTINT)
+            vec = v->domain->arch.hvm_domain.vpic.pics[0].irq_base;
+        else
+            vec =
+              v->domain->arch.hvm_domain.vioapic.redirtbl[0].RedirForm.vector;
 
-    if (type == APIC_DM_EXTINT)
-        pit_vec = v->domain->arch.hvm_domain.vpic.pics[0].irq_base;
-    else
-        pit_vec =
-          v->domain->arch.hvm_domain.vioapic.redirtbl[0].RedirForm.vector;
+        if (irq == vec)
+            return 1;
+    }
 
-    return (irq == pit_vec);
+    if (pt->irq == 8) { /* Or rtc irq? */
+        if (type == APIC_DM_EXTINT)
+            vec = v->domain->arch.hvm_domain.vpic.pics[1].irq_base;
+        else
+            vec =
+              v->domain->arch.hvm_domain.vioapic.redirtbl[8].RedirForm.vector;
+
+        if (irq == vec)
+            return is_rtc_periodic_irq(vrtc);
+    }
+
+    return 0;
 }
 
 int is_irq_enabled(struct vcpu *v, int irq)
 {
+    struct hvm_vioapic *vioapic = &v->domain->arch.hvm_domain.vioapic;
     struct hvm_virpic *vpic=&v->domain->arch.hvm_domain.vpic;
-        
+
+    if (vioapic->redirtbl[irq].RedirForm.mask == 0)
+       return 1;
+
     if ( irq & 8 ) {
         return !( (1 << (irq&7)) & vpic->pics[1].imr);
     }
index 56e54b40f05ecf9cfd23bce08c33a50cf05618e0..c83c2dd92aae75a0ddcdf9b0b2d5fe55bab17170 100644 (file)
@@ -315,17 +315,14 @@ void pickup_deactive_ticks(struct periodic_time *pt)
  * period: fire frequency in ns.
  */
 struct periodic_time * create_periodic_time(
-        PITChannelState *s,
         u32 period, 
         char irq,
-        char one_shot)
+        char one_shot,
+        time_cb *cb,
+        void *data)
 {
-    struct vcpu *v = s->vcpu;
-    struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
+    struct periodic_time *pt = &(current->domain->arch.hvm_domain.pl_time.periodic_tm);
     if ( pt->enabled ) {
-        if ( v->vcpu_id != 0 ) {
-            printk("HVM_PIT: start 2nd periodic time on non BSP!\n");
-        }
         stop_timer (&pt->timer);
         pt->enabled = 0;
     }
@@ -345,7 +342,8 @@ struct periodic_time * create_periodic_time(
     pt->scheduled = NOW() + period;
     set_timer (&pt->timer,pt->scheduled);
     pt->enabled = 1;
-    pt->priv = s;
+    pt->cb = cb;
+    pt->priv = data;
     return pt;
 }
 
index de9359a73b6ec350eb8107454d51bee4a43a2d67..da6224e9381332b3d7951db83e6647e9bd2f20fb 100644 (file)
@@ -683,7 +683,7 @@ void hvm_interrupt_post(struct vcpu *v, int vector, int type)
     struct  periodic_time *pt = 
         &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
 
-    if ( is_pit_irq(v, vector, type) ) {
+    if ( pt->enabled && is_periodic_irq(v, vector, type) ) {
         if ( !pt->first_injected ) {
             pt->pending_intr_nr = 0;
             pt->last_plt_gtime = hvm_get_guest_time(v);
@@ -694,8 +694,9 @@ void hvm_interrupt_post(struct vcpu *v, int vector, int type)
             pt->pending_intr_nr--;
             pt->last_plt_gtime += pt->period_cycles;
             hvm_set_guest_time(v, pt->last_plt_gtime);
-            pit_time_fired(v, pt->priv);
         }
+        if (pt->cb)
+            pt->cb(v, pt->priv);
     }
     
     switch(type) {
diff --git a/xen/arch/x86/hvm/rtc.c b/xen/arch/x86/hvm/rtc.c
new file mode 100644 (file)
index 0000000..210168c
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * QEMU MC146818 RTC emulation
+ * 
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <asm/mc146818rtc.h>
+#include <asm/hvm/vpit.h>
+#include <asm/hvm/io.h>
+#include <asm/hvm/support.h>
+#include <asm/current.h>
+
+/* #define DEBUG_RTC */
+
+void rtc_periodic_cb(struct vcpu *v, void *opaque)
+{
+    RTCState *s = opaque;
+    s->cmos_data[RTC_REG_C] |= 0xc0;
+}
+
+int is_rtc_periodic_irq(void *opaque)
+{
+    RTCState *s = opaque;
+    return !(s->cmos_data[RTC_REG_C] & RTC_AF || 
+           s->cmos_data[RTC_REG_C] & RTC_UF);
+}
+
+static void rtc_timer_update(RTCState *s, int64_t current_time)
+{
+    int period_code; 
+    int period;
+
+    period_code = s->cmos_data[RTC_REG_A] & 0x0f;
+    if (period_code != 0 && (s->cmos_data[RTC_REG_B] & RTC_PIE)) {
+        if (period_code <= 2)
+            period_code += 7;
+        
+        period = 1 << (period_code - 1); /* period in 32 Khz cycles */
+        period = DIV_ROUND((period * 1000000000ULL), 32768); /* period in ns */
+
+#ifdef DEBUG_RTC
+        printk("HVM_RTC: period = %uns\n", period);
+#endif
+
+        s->pt = create_periodic_time(period, RTC_IRQ, 0, rtc_periodic_cb, s);
+    } else if (s->pt) {
+        destroy_periodic_time(s->pt);
+        s->pt = NULL;
+    }
+}
+
+static void rtc_set_time(RTCState *s);
+
+static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    RTCState *s = opaque;
+
+    if ((addr & 1) == 0) {
+        s->cmos_index = data & 0x7f;
+        if (s->cmos_index < RTC_SIZE)
+            return 1;
+    } else if (s->cmos_index < RTC_SIZE) {
+#ifdef DEBUG_RTC
+        printk("HVM_RTC: write index=0x%02x val=0x%02x\n",
+               s->cmos_index, data);
+#endif        
+        switch(s->cmos_index) {
+        case RTC_SECONDS_ALARM:
+        case RTC_MINUTES_ALARM:
+        case RTC_HOURS_ALARM:
+            s->cmos_data[s->cmos_index] = data;
+            break;
+        case RTC_SECONDS:
+        case RTC_MINUTES:
+        case RTC_HOURS:
+        case RTC_DAY_OF_WEEK:
+        case RTC_DAY_OF_MONTH:
+        case RTC_MONTH:
+        case RTC_YEAR:
+            s->cmos_data[s->cmos_index] = data;
+            /* if in set mode, do not update the time */
+            if (!(s->cmos_data[RTC_REG_B] & RTC_SET)) {
+                rtc_set_time(s);
+            }
+            break;
+        case RTC_REG_A:
+            /* UIP bit is read only */
+            s->cmos_data[RTC_REG_A] = (data & ~RTC_UIP) |
+                (s->cmos_data[RTC_REG_A] & RTC_UIP);
+            rtc_timer_update(s, hvm_get_clock(s->vcpu));
+            break;
+        case RTC_REG_B:
+            if (data & RTC_SET) {
+                /* set mode: reset UIP mode */
+                s->cmos_data[RTC_REG_A] &= ~RTC_UIP;
+                data &= ~RTC_UIE;
+            } else {
+                /* if disabling set mode, update the time */
+                if (s->cmos_data[RTC_REG_B] & RTC_SET) {
+                    rtc_set_time(s);
+                }
+            }
+            s->cmos_data[RTC_REG_B] = data;
+            rtc_timer_update(s, hvm_get_clock(s->vcpu));
+            break;
+        case RTC_REG_C:
+        case RTC_REG_D:
+            /* cannot write to them */
+            break;
+        return 1;
+        }
+    }
+    return 0;
+}
+
+static inline int to_bcd(RTCState *s, int a)
+{
+    if (s->cmos_data[RTC_REG_B] & 0x04) {
+        return a;
+    } else {
+        return ((a / 10) << 4) | (a % 10);
+    }
+}
+
+static inline int from_bcd(RTCState *s, int a)
+{
+    if (s->cmos_data[RTC_REG_B] & 0x04) {
+        return a;
+    } else {
+        return ((a >> 4) * 10) + (a & 0x0f);
+    }
+}
+
+static void rtc_set_time(RTCState *s)
+{
+    struct tm *tm = &s->current_tm;
+    
+    tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
+    tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
+    tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
+    if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
+        (s->cmos_data[RTC_HOURS] & 0x80)) {
+        tm->tm_hour += 12;
+    }
+    tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
+    tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
+    tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
+    tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
+}
+
+static void rtc_copy_date(RTCState *s)
+{
+    const struct tm *tm = &s->current_tm;
+
+    s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
+    s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
+    if (s->cmos_data[RTC_REG_B] & 0x02) {
+        /* 24 hour format */
+        s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
+    } else {
+        /* 12 hour format */
+        s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
+        if (tm->tm_hour >= 12)
+            s->cmos_data[RTC_HOURS] |= 0x80;
+    }
+    s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
+    s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
+    s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
+    s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
+}
+
+/* month is between 0 and 11. */
+static int get_days_in_month(int month, int year)
+{
+    static const int days_tab[12] = { 
+        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 
+    };
+    int d;
+    if ((unsigned )month >= 12)
+        return 31;
+    d = days_tab[month];
+    if (month == 1) {
+        if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
+            d++;
+    }
+    return d;
+}
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct tm *tm)
+{
+    int days_in_month;
+
+    tm->tm_sec++;
+    if ((unsigned)tm->tm_sec >= 60) {
+        tm->tm_sec = 0;
+        tm->tm_min++;
+        if ((unsigned)tm->tm_min >= 60) {
+            tm->tm_min = 0;
+            tm->tm_hour++;
+            if ((unsigned)tm->tm_hour >= 24) {
+                tm->tm_hour = 0;
+                /* next day */
+                tm->tm_wday++;
+                if ((unsigned)tm->tm_wday >= 7)
+                    tm->tm_wday = 0;
+                days_in_month = get_days_in_month(tm->tm_mon, 
+                                                  tm->tm_year + 1900);
+                tm->tm_mday++;
+                if (tm->tm_mday < 1) {
+                    tm->tm_mday = 1;
+                } else if (tm->tm_mday > days_in_month) {
+                    tm->tm_mday = 1;
+                    tm->tm_mon++;
+                    if (tm->tm_mon >= 12) {
+                        tm->tm_mon = 0;
+                        tm->tm_year++;
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void rtc_update_second(void *opaque)
+{
+    RTCState *s = opaque;
+
+    /* if the oscillator is not in normal operation, we do not update */
+    if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
+        s->next_second_time += 1000000000ULL;
+        set_timer(&s->second_timer, s->next_second_time);
+    } else {
+        rtc_next_second(&s->current_tm);
+        
+        if (!(s->cmos_data[RTC_REG_B] & RTC_SET)) {
+            /* update in progress bit */
+            s->cmos_data[RTC_REG_A] |= RTC_UIP;
+        }
+        /* Delay time before update cycle */
+        set_timer(&s->second_timer2, s->next_second_time + 244000);
+    }
+}
+
+static void rtc_update_second2(void *opaque)
+{
+    RTCState *s = opaque;
+    struct hvm_domain *plat=&s->vcpu->domain->arch.hvm_domain;
+    struct hvm_virpic *pic= &plat->vpic;
+
+    if (!(s->cmos_data[RTC_REG_B] & RTC_SET)) {
+        rtc_copy_date(s);
+    }
+
+    /* check alarm */
+    if (s->cmos_data[RTC_REG_B] & RTC_AIE) {
+        if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
+             s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) &&
+            ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
+             s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) &&
+            ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
+             s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
+
+            s->cmos_data[RTC_REG_C] |= 0xa0; 
+            pic_set_irq(pic, s->irq, 0);
+            pic_set_irq(pic, s->irq, 1);
+        }
+    }
+
+    /* update ended interrupt */
+    if (s->cmos_data[RTC_REG_B] & RTC_UIE) {
+        s->cmos_data[RTC_REG_C] |= 0x90; 
+        pic_set_irq(pic, s->irq, 0);
+        pic_set_irq(pic, s->irq, 1);
+    }
+
+    /* clear update in progress bit */
+    s->cmos_data[RTC_REG_A] &= ~RTC_UIP;
+
+    s->next_second_time += 1000000000ULL;
+    set_timer(&s->second_timer, s->next_second_time);
+}
+
+static uint32_t rtc_ioport_read(void *opaque, uint32_t addr)
+{
+    RTCState *s = opaque;
+    struct hvm_domain *plat=&s->vcpu->domain->arch.hvm_domain;
+    struct hvm_virpic *pic= &plat->vpic;
+    int ret;
+
+    if ((addr & 1) == 0) {
+        return 0xff;
+    } else {
+        switch(s->cmos_index) {
+        case RTC_SECONDS:
+        case RTC_MINUTES:
+        case RTC_HOURS:
+        case RTC_DAY_OF_WEEK:
+        case RTC_DAY_OF_MONTH:
+        case RTC_MONTH:
+        case RTC_YEAR:
+            ret = s->cmos_data[s->cmos_index];
+            break;
+        case RTC_REG_A:
+            ret = s->cmos_data[s->cmos_index];
+            break;
+        case RTC_REG_C:
+            ret = s->cmos_data[s->cmos_index];
+            pic_set_irq(pic, s->irq, 0);
+            s->cmos_data[RTC_REG_C] = 0x00; 
+            break;
+        default:
+            ret = s->cmos_data[s->cmos_index];
+            break;
+        }
+#ifdef DEBUG_RTC
+        printk("HVM_RTC: read index=0x%02x val=0x%02x\n",
+               s->cmos_index, ret);
+#endif
+        return ret;
+    }
+}
+
+static int handle_rtc_io(ioreq_t *p)
+{
+    struct vcpu *v = current;
+    struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
+
+    if (p->size != 1 ||
+        p->pdata_valid ||
+        p->type != IOREQ_TYPE_PIO){
+        printk("HVM_RTC: wrong RTC IO!\n");
+        return 1;
+    }
+    
+    if (p->dir == 0) { /* write */
+        if (rtc_ioport_write(vrtc, p->addr, p->u.data & 0xFF))
+            return 1;
+    } else if (p->dir == 1 && vrtc->cmos_index < RTC_SIZE) { /* read */
+        p->u.data = rtc_ioport_read(vrtc, p->addr);
+        return 1;
+    }
+    return 0;
+}
+
+void rtc_init(struct vcpu *v, int base, int irq)
+{
+    RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
+
+    s->vcpu = v;
+    s->irq = irq;
+    s->cmos_data[RTC_REG_A] = 0x26;
+    s->cmos_data[RTC_REG_B] = 0x02;
+    s->cmos_data[RTC_REG_C] = 0x00;
+    s->cmos_data[RTC_REG_D] = 0x80;
+
+    s->current_tm = gmtime(get_localtime(v->domain));
+    rtc_copy_date(s);
+
+    init_timer(&s->second_timer, rtc_update_second, s, v->processor);
+    init_timer(&s->second_timer2, rtc_update_second2, s, v->processor);
+
+    s->next_second_time = NOW() + 1000000000ULL;
+    set_timer(&s->second_timer2, s->next_second_time);
+
+    register_portio_handler(base, 2, handle_rtc_io);
+}
+
+void rtc_deinit(struct domain *d)
+{
+    RTCState *s = &d->arch.hvm_domain.pl_time.vrtc;
+
+    kill_timer(&s->second_timer);
+    kill_timer(&s->second_timer2);
+}
index 0f30bd5478c176dc04e337d8e5daae373c6eabdc..badc694881f253c1958899a9a66a5389d663cd1f 100644 (file)
@@ -140,8 +140,8 @@ asmlinkage void svm_intr_assist(void)
         case APIC_DM_FIXED:
         case APIC_DM_LOWEST:
             /* Re-injecting a PIT interruptt? */
-            if (re_injecting && 
-                is_pit_irq(v, intr_vector, intr_type)) {
+            if (re_injecting && pt->enabled && 
+                is_periodic_irq(v, intr_vector, intr_type)) {
                     ++pt->pending_intr_nr;
             }
             /* let's inject this interrupt */
index 120698edb9269aa49ab8f413bdbe63286466b770..4cf43f2e109e9774b5463f47be7b42b3d18b55c5 100644 (file)
@@ -921,6 +921,7 @@ static void svm_relinquish_guest_resources(struct domain *d)
     }
 
     kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
+    rtc_deinit(d);
 
     if ( d->arch.hvm_domain.shared_page_va )
         unmap_domain_page_global(
@@ -935,6 +936,7 @@ static void svm_migrate_timers(struct vcpu *v)
 {
     struct periodic_time *pt = 
         &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
+    struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
 
     if ( pt->enabled )
     {
@@ -943,6 +945,8 @@ static void svm_migrate_timers(struct vcpu *v)
     }
     if ( VLAPIC(v) != NULL )
         migrate_timer(&VLAPIC(v)->vlapic_timer, v->processor);
+    migrate_timer(&vrtc->second_timer, v->processor);
+    migrate_timer(&vrtc->second_timer2, v->processor);
 }
 
 
index d5a2923055b5f18be686f7fee0e4217f3c6c4a41..43cbb48c1929dea1027a8dcf011b4eef2db6e72b 100644 (file)
@@ -146,6 +146,7 @@ static void vmx_relinquish_guest_resources(struct domain *d)
     }
 
     kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
+    rtc_deinit(d);
 
     if ( d->arch.hvm_domain.shared_page_va )
         unmap_domain_page_global(
@@ -487,6 +488,7 @@ static void stop_vmx(void)
 void vmx_migrate_timers(struct vcpu *v)
 {
     struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
+    struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
 
     if ( pt->enabled )
     {
@@ -495,6 +497,8 @@ void vmx_migrate_timers(struct vcpu *v)
     }
     if ( VLAPIC(v) != NULL )
         migrate_timer(&VLAPIC(v)->vlapic_timer, v->processor);
+    migrate_timer(&vrtc->second_timer, v->processor);
+    migrate_timer(&vrtc->second_timer2, v->processor);
 }
 
 static void vmx_store_cpu_guest_regs(
index 6191af3ef350888a229f538d3f75ca5c0ee094bd..970a742efc4a781d0469b28912c8871e47a29e58 100644 (file)
@@ -919,6 +919,13 @@ void send_timer_event(struct vcpu *v)
     send_guest_vcpu_virq(v, VIRQ_TIMER);
 }
 
+/* Return secs after 00:00:00 localtime, 1 January, 1970. */
+unsigned long get_localtime(struct domain *d)
+{
+    return wc_sec + (wc_nsec + NOW()) / 1000000000ULL 
+        + d->time_offset_seconds;
+}
+
 /*
  * Local variables:
  * mode: C
index 130e2b438b5241d8e0534b20360a0817fe358ca2..b076e2a6a00cb124584825a8a8eee9aca0202b34 100644 (file)
@@ -20,8 +20,9 @@ obj-y += softirq.o
 obj-y += string.o
 obj-y += symbols.o
 obj-y += sysctl.o
-obj-y += trace.o
+obj-y += time.o
 obj-y += timer.o
+obj-y += trace.o
 obj-y += version.o
 obj-y += vsprintf.o
 obj-y += xmalloc.o
diff --git a/xen/common/time.c b/xen/common/time.c
new file mode 100644 (file)
index 0000000..65929d1
--- /dev/null
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * time.c
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <xen/config.h>
+#include <xen/time.h>
+
+/* Nonzero if YEAR is a leap year (every 4 years,
+   except every 100th isn't, and every 400th is).  */
+#define __isleap(year) \
+  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+/* How many days are in each month.  */
+const unsigned short int __mon_lengths[2][12] = {
+    /* Normal years.  */
+    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+    /* Leap years.  */
+    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+#define SECS_PER_HOUR (60 * 60)
+#define SECS_PER_DAY  (SECS_PER_HOUR * 24)
+
+struct tm gmtime(unsigned long t)
+{
+    struct tm tbuf;
+    long days, rem;
+    int y;
+    unsigned short int *ip;
+
+    days = t / SECS_PER_DAY;
+    rem = t % SECS_PER_DAY;
+
+    tbuf.tm_hour = rem / SECS_PER_HOUR;
+    rem %= SECS_PER_HOUR;
+    tbuf.tm_min = rem / 60;
+    tbuf.tm_sec = rem % 60;
+    /* January 1, 1970 was a Thursday.  */
+    tbuf.tm_wday = (4 + days) % 7;
+    if ( tbuf.tm_wday < 0 )
+        tbuf.tm_wday += 7;
+    y = 1970;
+    while ( days >= (rem = __isleap(y) ? 366 : 365) )
+    {
+        ++y;
+        days -= rem;
+    }
+    while ( days < 0 )
+    {
+        --y;
+        days += __isleap(y) ? 366 : 365;
+    }
+    tbuf.tm_year = y - 1900;
+    tbuf.tm_yday = days;
+    ip = (unsigned short int *)__mon_lengths[__isleap(y)];
+    for ( y = 0; days >= ip[y]; ++y )
+        days -= ip[y];
+    tbuf.tm_mon = y;
+    tbuf.tm_mday = days + 1;
+    tbuf.tm_isdst = -1;
+
+    return tbuf;
+}
index f6a488e339389d17bb7650fde89897775a7f51db..8e2d386e59e4e0a08fb9cbaf3347cafa2d6dff0c 100644 (file)
@@ -75,7 +75,7 @@ void pic_update_irq(struct hvm_virpic *s); /* Caller must hold s->lock */
 uint32_t pic_intack_read(struct hvm_virpic *s);
 void register_pic_io_hook (void);
 int cpu_get_pic_interrupt(struct vcpu *v, int *type);
-int is_pit_irq(struct vcpu *v, int irq, int type);
+int is_periodic_irq(struct vcpu *v, int irq, int type);
 int is_irq_enabled(struct vcpu *v, int irq);
 void do_pic_irqs (struct hvm_virpic *s, uint16_t irqs);
 void do_pic_irqs_clear (struct hvm_virpic *s, uint16_t irqs);
index eb3ccd12072a0f2c393d8987ab515513d18f4d60..83b1af2622dd90278af4dce13128d322a784e0d5 100644 (file)
 #include <xen/lib.h>
 #include <xen/time.h>
 #include <xen/errno.h>
+#include <xen/time.h>
 #include <xen/timer.h>
 #include <asm/hvm/vpic.h>
 
 #define PIT_FREQ 1193181
-#define PIT_BASE        0x40
+#define PIT_BASE 0x40
 
 typedef struct PITChannelState {
     int count; /* can be 65536 */
@@ -49,10 +50,32 @@ typedef struct PITChannelState {
     struct vcpu      *vcpu;
     struct periodic_time *pt;
 } PITChannelState;
+
+typedef struct PITState {
+    PITChannelState channels[3];
+    int speaker_data_on;
+    int dummy_refresh_clock;
+} PITState;
+
+#define RTC_SIZE 14
+typedef struct RTCState {
+    uint8_t cmos_data[RTC_SIZE];  /* Only handle time/interrupt part in HV */
+    uint8_t cmos_index;
+    struct tm current_tm;
+    int irq;
+    /* second update */
+    int64_t next_second_time;
+    struct timer second_timer;
+    struct timer second_timer2;
+    struct vcpu      *vcpu;
+    struct periodic_time *pt;
+} RTCState;
    
 /*
  * Abstract layer of periodic time, one short time.
  */
+typedef void time_cb(struct vcpu *v, void *opaque);
+
 struct periodic_time {
     char enabled;               /* enabled */
     char one_shot;              /* one shot time */
@@ -64,19 +87,15 @@ struct periodic_time {
     s_time_t scheduled;         /* scheduled timer interrupt */
     u64 last_plt_gtime;         /* platform time when last IRQ is injected */
     struct timer timer;         /* ac_timer */
+    time_cb *cb;
     void *priv;                 /* ponit back to platform time source */
 };
 
-typedef struct PITState {
-    PITChannelState channels[3];
-    int speaker_data_on;
-    int dummy_refresh_clock;
-} PITState;
-
 struct pl_time {    /* platform time */
     struct periodic_time periodic_tm;
     struct PITState      vpit;
-    /* TODO: RTC/ACPI time */
+    struct RTCState      vrtc;
+    /* TODO: ACPI time */
 };
 
 static __inline__ s_time_t get_scheduled(
@@ -90,13 +109,30 @@ static __inline__ s_time_t get_scheduled(
         return -1;
 }
 
+extern u64 hvm_get_guest_time(struct vcpu *v);
+/*
+ * get processor time.
+ * unit: TSC
+ */
+static __inline__ int64_t hvm_get_clock(struct vcpu *v)
+{
+    uint64_t  gtsc;
+
+    gtsc = hvm_get_guest_time(v);
+    return gtsc;
+}
+
+#define ticks_per_sec(v)      (v->domain->arch.hvm_domain.tsc_frequency)
+
 /* to hook the ioreq packet to get the PIT initialization info */
 extern void hvm_hooks_assist(struct vcpu *v);
 extern void pickup_deactive_ticks(struct periodic_time *vpit);
-extern u64 hvm_get_guest_time(struct vcpu *v);
-extern struct periodic_time *create_periodic_time(PITChannelState *v, u32 period, char irq, char one_shot);
+extern struct periodic_time *create_periodic_time(u32 period, char irq, char one_shot, time_cb *cb, void *data);
 extern void destroy_periodic_time(struct periodic_time *pt);
 void pit_init(struct vcpu *v, unsigned long cpu_khz);
+void rtc_init(struct vcpu *v, int base, int irq);
+void rtc_deinit(struct domain *d);
+int is_rtc_periodic_irq(void *opaque);
 void pt_timer_fn(void *data);
 void pit_time_fired(struct vcpu *v, void *priv);
 
index 931401388e0add53385bb0134850b0d23f2ed5ed..33b12763ed4701edff693f87745783a0e2e40179 100644 (file)
@@ -49,6 +49,20 @@ struct domain;
 typedef s64 s_time_t;
 
 s_time_t get_s_time(void);
+unsigned long get_localtime(struct domain *d);
+
+struct tm {
+    int     tm_sec;         /* seconds */
+    int     tm_min;         /* minutes */
+    int     tm_hour;        /* hours */
+    int     tm_mday;        /* day of the month */
+    int     tm_mon;         /* month */
+    int     tm_year;        /* year */
+    int     tm_wday;        /* day of the week */
+    int     tm_yday;        /* day in the year */
+    int     tm_isdst;       /* daylight saving time */
+};
+struct tm gmtime(unsigned long t);
 
 #define NOW()           ((s_time_t)get_s_time())
 #define SECONDS(_s)     ((s_time_t)((_s)  * 1000000000ULL))